/* A wee progam to test the spangling sockets. */

/*#define _P __P*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <nd.h>
#include <sys/timeb.h>

#define SIOCLRP 0x8945

#define SOCKBUF 400000 //(64*1024)

#ifdef USER_STACK
#include "libusernet.h"
#else
#define user_init() ((void)0)
#define user_socket(a,b,c) socket(a,b,c)
#define user_bind(a,b,c) bind(a,b,c)
#define user_connect(a,b,c) connect(a,b,c)
#define user_recv(a,b,c,d) recv(a,b,c,d)
#define user_shutdown(a,b) shutdown(a,b)
#define user_close(a) close(a)
#define user_kill() ((void)0)
#define user_listen(a,b) listen(a,b)
#define user_accept(a,b,c) accept(a,b,c)
#define user_setsockopt(a,b,c,d,e) setsockopt(a,b,c,d,e)
#endif

#define RECV_LENGTH 8192 /*16348*/
#define MAX_CONNS   30

void *recver(void *arg);

unsigned int csum_partial(const unsigned char * buff, 
                          int len, unsigned int sum);

/*
 * Variables shared between all threads.
 */
int touch_data;
struct hostent *he;

char *eth = "eth0"; 
char *sourceIp; 
int my_port; 

extern struct net_device** ultcp_interface;
extern char ultcp_cnt;

int             sock;
int             num_conns = 0;
#ifdef USER_STACK
pthread_t       threads[30];
#endif
int                port;

long tvdif2us(struct timeval *t0, struct timeval *t1)
{
  struct timeval tdiff;
  long us;

  tdiff.tv_sec = t1->tv_sec - t0->tv_sec;
  tdiff.tv_usec = t1->tv_usec - t0->tv_usec;
  if (tdiff.tv_usec < 0)
    tdiff.tv_sec--, tdiff.tv_usec += 1000000;

  us = ((long)tdiff.tv_sec*1000000) + tdiff.tv_usec;
  return us;
}


int main(int argc, char *argv[])
{
    char               buf[2];
    int                i;
    int                ret;
    struct sockaddr_in laddr;

    /*
     * Initialise the stack as early as possible. It does some gross memory
     * mapping that is necessary for us not to crash!
     */

    user_init();

    /*
     * Parse the command line.
     */

    if((argc < 2))
    {
	fprintf(stderr, "%s: Usage: %s listen.ip advertised_interfaces\n", 
		argv[0], argv[0]);
	exit(EXIT_FAILURE);
    }

    port = strtol(argv[1], NULL, 10);
    if ( errno == ERANGE || port > 65535 || port < 0 )
    {
	fprintf(stderr, "%s: port \"%s\" invalid.\n", argv[0], argv[1]);
	exit(EXIT_FAILURE);
    }

    /*
     * Create and bind the listener.
     */

    printf("%s: listening on port %d.\n",   argv[0], port);
    laddr.sin_addr.s_addr = 0;
    laddr.sin_port        = htons(port);
    laddr.sin_family      = AF_INET;

    sock = user_socket(AF_INET, SOCK_STREAM, 0);
    if ( sock < 0 )
    {
	fprintf(stderr, "%s: socket failed: %d (%s)\n",
		argv[0], errno, strerror(errno));
	exit(EXIT_FAILURE);
    }

// Make the socket non-blocking
    user_fcntl(sock, F_SETFL, O_NONBLOCK); 

    if (argc>2){
      int i,j;
      //we have specific interfaces!
      for (i=2;i<argc;i++){
	for (j=0;j<ultcp_cnt;j++)
	  if (!strcmp(argv[i],ultcp_interface[j]->name))
	      break;
	
	if (j==ultcp_cnt){
	  printf("Can't find interface %s\n",argv[i]);
	  exit(1);
	}
	  
	laddr.sin_addr.s_addr = ultcp_interface[j]->ip_addr;
	ret = user_bind(sock, 
		    (struct sockaddr *)&laddr, 
		    sizeof(struct sockaddr_in));
	if ( ret < 0 )	{
	  fprintf(stderr, "%s: bind failed: %d (%s)\n",
		  argv[0], errno, strerror(errno));
	  exit(EXIT_FAILURE);
	}
      }
    }
    else {
      ret = user_bind(sock, 
		    (struct sockaddr *)&laddr, 
		    sizeof(struct sockaddr_in));
      if ( ret < 0 )	{
	fprintf(stderr, "%s: bind failed: %d (%s)\n",
		argv[0], errno, strerror(errno));
	exit(EXIT_FAILURE);
      }
    }

    ret = user_listen(sock, 5);
    if ( ret < 0 )
    {
	fprintf(stderr, "%s: listen failed: %d (%s)\n",
		argv[0], errno, strerror(errno));
	exit(EXIT_FAILURE);
    }

    /*
     * We accept() in each thread. After accepting, a thread spawns so that
     * connections can always be accepted.
     */
    recver(0);

    printf("close: \t\t");
    //    ret = user_close(sock);
    printf("%d\n", ret);

    user_kill();

    exit(EXIT_SUCCESS);
}


void *recver(void *arg)
{
  int                conn,i;
    struct sockaddr_in raddr;
    int                addrlen = sizeof(struct sockaddr_in);
    int                fd[10];
    int                old_totals, totals, calls;
    struct timeval     old_time, curr_time;
    int                received, ret;
    char               buf[RECV_LENGTH];
    fd_set readfds;
    struct timeval tv1;
    int max_fd;
    int total_recv[10];
    int cnt = 0,j;
    unsigned int start;
    unsigned int last_print;
    int read_print;
    struct timeb tb;
    conn = -1;

    /*
     * Accept a connection.
     */

    for (i=0;i<10;i++)
      total_recv[i] = 0;

    printf("Waiting for accept...\n");

    cnt = 0;
    while (1){
      my_upcall_worker(NULL);

      FD_ZERO(&readfds);
      FD_SET(sock,&readfds);
      max_fd = sock;

      for (i=0;i<=conn;i++){
	FD_SET(fd[i],&readfds);

	if (fd[i]>max_fd)
	  max_fd = fd[i];
      }

      tv1.tv_sec = 0;
      tv1.tv_usec = 0;

      user_select(max_fd+1,&readfds, NULL, NULL, &tv1); 

      if (FD_ISSET(sock,&readfds)){
	//	if (conn==1)
	  {fd[++conn] = user_accept(sock, (struct sockaddr *)&raddr, &addrlen);
	    printf("Accept sock %d on connection %d\n",fd[conn],conn);
	    ftime(&tb);
	    start = tb.time*1000+tb.millitm;
	    read_print = 0;
	    last_print = start;
	    if (fd[conn]<0){
	      fprintf(stderr, "Accept failed: %d (%s)\n", errno, strerror(errno));
	      exit(1);
	    }
	    user_fcntl(fd[conn], F_SETFL, O_NONBLOCK); 
	  }
      }

      for (i=0;i<=conn;i++)
	if (FD_ISSET(fd[i],&readfds)){
	  do {
	    received = user_recv(fd[i], buf, RECV_LENGTH, MSG_DONTWAIT);
	    if ( received < 0 ){
	      if (errno == EWOULDBLOCK){
	      //printf("Got EWOULDBLOCK in conn %d after transferring %d bytes...", received);
	      //fflush(stdout);
		break;
	      }
	      else {
		fprintf(stderr, "Read failed: %d (%s)\n", errno, strerror(errno));
		exit(1);
	      }
	    }
	    
	    //do checksum!
	    /*	    for (j=0;j<received;j++){
	      if (buf[j]!=(char)(cnt%14000)){
		int min = 0,max = received;
		printf("Checksum error at byte %d, pos %d\n",cnt,j);
		if (min<j-10)
		  min = j-10;
		if (max>j+10)
		  max = j+10;
		for (j=min;j<max;j++)
		  printf("%d ",buf[j]);
		exit(1);
	      }
	      cnt = (cnt+1);
	      }*/
	  
	    read_print += received;
	    if (total_recv[i]>>17 != (total_recv[i]+received)>>17){
	      unsigned int delta,elapsed;
	      ftime(&tb);

	      elapsed = tb.time*1000+tb.millitm-last_print;
	      last_print = tb.time*1000+tb.millitm;
	      delta = last_print-start;
	      
	      total_recv[i] += received;
	      printf("[%d]=%.1fMB\t%.3f\t%.6f\n",i,(float)total_recv[i]/(1<<20),(float)total_recv[i]*1000/delta/(1<<20),(float)read_print*1000/elapsed/(1<<20));
	      read_print = 0;
	    }
	    else 
	      total_recv[i] += received;

	  }while (1);
	}
    }
} 

/* End of $RCSFile$ */
